﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Xml.Linq;
using Medianamik.Core.Configuration;
using System.Configuration;

namespace Medianamik.Core.Urls
{
    public class UrlXmlDoc
    {
        private readonly Guid? _parentId;
        private readonly string _hierarchicalPath;
        private readonly DateTime _createdOn;
        private readonly short _position;
        private readonly int _hierarchicalDepth;
        private readonly bool _linkOnFirstChild;

        private static readonly MedianamikConfigSection Config =
            (MedianamikConfigSection)ConfigurationManager.GetSection(ConfigurationSectionName.MEDIANAMIK);

        private const string NODEID_ATT = "nodeid";
        private const string WESITE_ATT = "website";
        private const string PATH_ATT = "path";
        private const string EXCLUDED_ATT = "excluded";

        public DateTime CreatedOn
        {
            get { return _createdOn; }
        }

        public bool LinkOnFirstChild
        {
            get { return _linkOnFirstChild; }
        }

        public int HierarchicalDepth
        {
            get { return _hierarchicalDepth; }
        }

        public short Position
        {
            get { return _position; }
        }

        public string HierarchicalPath
        {
            get { return _hierarchicalPath; }
        }

        public Guid? ParentId
        {
            get { return _parentId; }
        }

        public Guid NodeId { get; private set; }
        public Guid NodeTypeId { get; private set; }
        public CultureInfo Culture { get; private set; }
        public int? Revision { get; private set; }
        public int Generation { get; private set; }
        public XDocument Xml { get; private set; }

        public UrlXmlDoc(Guid pNodeId, Guid pNodeTypeId,
            Guid? parentId, string hierarchicalPath, DateTime createdOn,
            short position, int hierarchicalDepth, bool linkOnFirstChild,
            CultureInfo pCulture, int? pRevision, int pGeneration, string pXml)
        {
            _parentId = parentId;
            _hierarchicalPath = hierarchicalPath;
            _createdOn = createdOn;
            _position = position;
            _hierarchicalDepth = hierarchicalDepth;
            _linkOnFirstChild = linkOnFirstChild;
            NodeId = pNodeId;
            NodeTypeId = pNodeTypeId;
            Culture = pCulture;
            Revision = pRevision;
            Generation = pGeneration;
            Xml = string.IsNullOrEmpty(pXml) ? new XDocument() : XDocument.Parse(pXml);
        }

        public IEnumerable<IUrlDictionaryEntry> GetIUrlDictionaryEntries(List<Domain> pDomains, List<PageTemplate> pPageTemplates)
        {
            if (Xml == null
                || Xml.Root == null)
            {
                return Enumerable.Empty<IUrlDictionaryEntry>();
            }
            return Xml.Root.Elements("url").SelectMany(e => ExtractIUrlDictionayEntries(e, pDomains, pPageTemplates, this.Culture.Name));

        }

        private string ExtractPath(XElement pElm)
        {
            return (string)pElm.Attribute(PATH_ATT) ?? string.Empty;
        }

        private List<Domain> ExtractDomains(XElement pElm, List<Domain> pDomains)
        {
            var websiteId = ExtractWebsiteId(pElm);
            return pDomains.FindAll(d => d.WebsiteId == websiteId);
        }

        private IEnumerable<IUrlDictionaryEntry> ExtractIUrlDictionayEntries(XElement pElm, 
            List<Domain> pDomains, List<PageTemplate> pPageTemplates, string pCultureName)
        {
            var path = ExtractPath(pElm);
            var realNodeId = ExtractNodeId(pElm);
            var excludedFromIndexation = ExtractExcludedFromIndexation(pElm);
            var goodDomains = ExtractDomains(pElm, pDomains).Where(d => d.DomainWebSite != null);

            var isInProductionMode = 
                Config.ApplicationEnvironment == Bootstrapping.ApplicationEnvironment.Production ||
                Config.ApplicationEnvironment == Bootstrapping.ApplicationEnvironment.Auto;

            foreach (var domain in goodDomains)
            {
                var pageTemplate = ExtractPageTemplate(pPageTemplates, domain);

                if (pageTemplate != null)
                {
                    var domainWebsite = domain.DomainWebSite;

                    if ((String.IsNullOrEmpty(domain.Culture) || 
                        domain.Culture.EqualsIgnoreCase(Culture.Name)) && 
                        domainWebsite.Instances.Exists(pCultureName))
                    {
                        yield return new UrlDictionaryEntry(realNodeId, NodeTypeId,
                            ParentId, HierarchicalPath, ((long)(DateTime.MaxValue - CreatedOn).TotalSeconds) | (Position << 48), HierarchicalDepth, LinkOnFirstChild,
                            domainWebsite, Culture, Revision, Generation, 
                            isInProductionMode && pageTemplate.IsHttps ? "https://" : "http://", 
                            domain.Name,
                            domain.DomainGroup, domain.Culture, path, pageTemplate.TemplatePath,
                            excludedFromIndexation);
                    }
                }
            }

        }

        private Guid ExtractWebsiteId(XElement pElm)
        {
            var websiteId = (Guid?)pElm.Attribute(WESITE_ATT) ?? Guid.Empty;
            return websiteId;
        }

        private bool ExtractExcludedFromIndexation(XElement pElm)
        {
            var result = false;
            var xmlResult = pElm.Attribute(EXCLUDED_ATT);

            if (xmlResult != null)
                Boolean.TryParse(xmlResult.Value, out result);

            return result;
        }

        /// <summary>
        /// Le NodeId n'est pas forcément l'ID du Noeud en cours
        /// Dans la cas d'un noeud de syndication par exemple
        /// </summary>
        private Guid ExtractNodeId(XElement pElm)
        {
            Guid? nodeId = (Guid?)pElm.Attribute(NODEID_ATT);
            if (!nodeId.HasValue)
            {
                //Dans ce cas on retourne le NodeId du Noeud courant
                nodeId = NodeId;
            }

            return nodeId.Value;
        }

        private PageTemplate ExtractPageTemplate(List<PageTemplate> pPageTemplates, Domain pDomain)
        {
            NodeType nodeType = NodeTypeManager.GetNodeType(NodeTypeId);
            if (nodeType == null || nodeType.Templates.Count == 0)
            {
                return null;
            }

            var typeTemplate = nodeType.Templates.Find(t => t.WebSiteId == pDomain.WebsiteId);
            if (typeTemplate == null)
            {
                return null;
            }

            var pageTemplate = pPageTemplates.Find(pt => pt.ID == typeTemplate.TemplateId);

            return pageTemplate;
        }
    }

}

